package com.naiterui.ehp.bs.doctor.service.impl;

import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.naiterui.ehp.bp.bo.patient.MsgMaPublishParamBO;
import com.naiterui.ehp.bp.bo.recommend.MedicationRecomBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.domain.Doctor;
import com.naiterui.ehp.bp.domain.Patient;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bp.utils.lang.ConfigUtil;
import com.naiterui.ehp.bs.doctor.fegin.PatientFeignClient;
import com.naiterui.ehp.bs.doctor.service.IDoctorService;
import com.naiterui.ehp.bs.doctor.service.IMedicationRecomSerivce;
import com.naiterui.ehp.bs.doctor.service.IPatientService;
import com.naiterui.ehp.bs.doctor.service.IRePurchaseService;
import com.naiterui.ehp.bs.doctor.utils.PropertyValueConstants;
import com.naiterui.ehp.bs.doctor.utils.RedisUtils;

import cn.hutool.json.JSONUtil;

/**
 * 复购实现
 *
 * @author fanjd
 * @since 1.0.0
 */
@Service
public class AdRePurchaseServiceImpl implements IRePurchaseService {
    /**
     * 日志对象：<code>logger</code>
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(AdRePurchaseServiceImpl.class);
    // 1小时
    private static final Integer ONE_HOUR = 1;
    // 可配置时间
    private static final Integer CONFIGURABLE_HOUR = 2;
    // 推荐未成单使用微信固定type
    private static final String WX_TYPE = ConfigUtil.getString("recom_wx_type");
    // 推荐未成单使用微信固定key
    private static final String WX_KEY = ConfigUtil.getString("recom_wx_key");
    // b2c服务地址
    @Autowired
    private IMedicationRecomSerivce medicationRecomService;
    @Autowired
    private IDoctorService doctorService;
    @Autowired
    private IPatientService patientService;
    @Autowired
    private PatientFeignClient patientFeignClient;

    /**
     * 推荐1小时、24小时未购买发送微信通知
     *
     * @param status
     *
     * @Author fanjd
     * @Date 2017年6月14日
     * @since 1.0.0
     */
    @Override
    public void getRecomRemind(Integer status) {
        // 当前时间
        Date start = null;
        // 查询开始的时间
        Date end = null;
        // 在当前时间之前指定分钟数
        String beforeNow = "";
        // 在结束时间之前指定分钟数
        String beforeEnd = "";
        String timeStr = "";
        if (ONE_HOUR.equals(status)) {
            beforeNow = ConfigUtil.getString("recom_the_other_minute");
            beforeEnd = ConfigUtil.getString("recom_deviation_minute");
            timeStr = "1小时前";
        } else if (CONFIGURABLE_HOUR.equals(status)) {
            beforeNow = ConfigUtil.getString("recom_before_time");
            beforeEnd = ConfigUtil.getString("recom_before_end_time");
            timeStr = "24小时前";
        }
        // 获取当前'yyyy-MM-dd HH:mm:ss'的Date时间
        Date now = DateUtil.parseStrToDateTime(DateUtil.getCurrentFormatDateTime());
        // 推荐多少分钟后未购买提醒
        // 获取当前时间指定分钟数
        end = DateUtil.minusMinutes(now, Integer.parseInt(beforeNow));
        // 获取当前时间指定分钟数前十分钟
        start = DateUtil.minusMinutes(end, Integer.parseInt(beforeEnd));
        // 获取指定日期的推荐记录集合
        LOGGER.info("获取指定日期的推荐未购买记录: starTime:{},endTime:{},status:{}", DateUtil.parseDateToString(start, DateUtil.DATE_TIME_FORMAT),
                    DateUtil.parseDateToString(end, DateUtil.DATE_TIME_FORMAT), status);
        List<MedicationRecomBO> noOrderRecomList = this.medicationRecomService
            .getSpecifydayRecom(Arrays.asList(PropertyValueConstants.DOCTOR_RECOM_NOT_BUY, 2), start,
                                                                                                  end);
        if (null != noOrderRecomList && !noOrderRecomList.isEmpty()) {
            LOGGER.info("推荐未成单的个数,num:{},status:{}", noOrderRecomList.size(), status);
            // 获取医生、患者信息
            Set<Long> doctorIds = new HashSet<>();
            Set<Long> patientIds = new HashSet<>();
            for (MedicationRecomBO medicationRecom : noOrderRecomList) {
                doctorIds.add(medicationRecom.getDoctorId());
                patientIds.add(medicationRecom.getPatientId());
            }
            Map<Long, Patient> patientMap = this.patientService.getPatientMap(patientIds);
            Map<Long, Doctor> doctorMap = this.doctorService.getDoctorMap(doctorIds);

            // 处理发送购药提醒
            if (null == patientMap || null == doctorMap) {
                LOGGER.info("recomRemind 推荐用药未购提醒失败！ 推荐对应医生患者用户为空  : patientIds {},doctorIds {}", patientIds.toString(),
                            doctorIds.toString());
            }
            for (MedicationRecomBO medicationRecom : noOrderRecomList) {
                Doctor tempDoctor = doctorMap.get(medicationRecom.getDoctorId());
                Patient tempPatient = patientMap.get(medicationRecom.getPatientId());
                // 作废的直接跳过
                if (null == tempDoctor || null == tempPatient || medicationRecom.getInvalid().equals(MedicationRecomBO.RECOM_INVALID_YEAH)) {
                    continue;
                }
                String recomContent = tempDoctor.getName() + "医生" + timeStr + "为您提供了用药建议，请及时查看。";
                Map<String, Object> paramMap = new HashMap<>();
                paramMap.put("inquirerName", tempPatient.getName());
                paramMap.put("prescriptionTime",
                    DateUtil.formatDate(medicationRecom.getCreatedAt(), DateUtil.DATE_FORMAT_BY_DIAGONAL_MIN));
                paramMap.put("remark", recomContent);
                String path = null;
                // 处方详情
                if (medicationRecom.getStatus() == 0) {
                    path = "pages/recipeDetail/recipeDetail?recomId=" + medicationRecom.getId();
                } else if (medicationRecom.getStatus() == 2) {
                    path = "pages/orderDetail/orderDetail?orderId=" + medicationRecom.getOrderId();
                }
                this.patientFeignClient.publishMaMessage(MsgMaPublishParamBO.builder().doctorId(tempDoctor.getId())
                    .patientId(tempPatient.getId()).path(path).msgType(MsgMaPublishParamBO.MsgType.PRESCRIPTION_PAY)
                    .content(JSONUtil.toJsonStr(paramMap)).build());
                // Map<String, Object> paramMap = new HashMap<>();
                // paramMap.put("recomId", medicationRecom.getId());
                // paramMap.put("recomContent", recomContent);
                // paramMap.put("patientId", medicationRecom.getPatientId());
                // this.patientFeignClient.patientPush(PatientFeignClient.PATIENT_PUSH_PLATFORMID,
                // PatientFeignClient.PATIENT_PUSH_TEMPLATED_RECOMMEND_NOT_ORDER, JSON.toJSONString(paramMap));
            }
        } else {
            LOGGER.info("未有需要提醒的推荐记录,任务类型status:{} ", status);
        }
    }

    /**
     * 判断当前时间距离发送时间是否小于30天，小于将发送时间改为下个周期
     *
     * @param patientId
     *
     * @Author fanjd
     * @Date 2017年6月12日
     * @see IRePurchaseService#updateSendCycle(java.lang.Long)
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateSendCycle(Long patientId) {
        // 取出患者的发送时间--缓存存的是0点的时间戳
        Double sendTime = RedisUtils.zscore(CommonConstant.PATIENT_RE_PURCHASE_KEY, patientId + "");
        // 判断当前患者是否已经成单过，如果sendTime为空，就是当前患者之前没有提交过订单
        if (null != sendTime) {
            DecimalFormat df = new DecimalFormat("#");
            // 将科学计数法形式的double值转换成long型的时间戳
            long sendTimeStamp = Long.parseLong(df.format(sendTime));
            // 当天0点的时间戳
            long nowTimeStamp = this.getNowTimeStamp();
            // 发送时间和当前0点的时间戳之差
            long difference = sendTimeStamp - nowTimeStamp;
            // 获取发送周期的时间戳
            long sendCycleTimeStamp = this.getSendCycleTimeStamp();
            // 当前时间距离发送时间小于等于1个周期(v2.8 30天)
            if (difference <= sendCycleTimeStamp) {
                // 30天类有复购行为，将发送时间增量改为下个周期
                // 提前计算周期
                double nextSendTimeStamp = this.nextSendTimeStamp(sendTimeStamp);
                RedisUtils.zadd(CommonConstant.PATIENT_RE_PURCHASE_KEY, nextSendTimeStamp, patientId + "");
            }
        }
    }

    /**
     * 删除过期任务
     *
     * @Author fanjd
     * @Date 2017年6月19日
     * @see IRePurchaseService#removeExpireTask()
     * @since 1.0.0
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeExpireTask() {
        // 当天0点的时间戳
        long nowTimeStamp = this.getNowTimeStamp();
        // 获取发送周期的时间戳
        long sendCycleTimeStamp = this.getSendCycleTimeStamp();
        // 2个周期之前的时间戳
        long start = nowTimeStamp - 2 * sendCycleTimeStamp;
        // 1个周期之前的时间戳
        long end = nowTimeStamp - sendCycleTimeStamp;
        double min = Double.parseDouble(start + "");
        double max = Double.parseDouble(end + "");
        Long num = RedisUtils.zremrangeByScore(CommonConstant.PATIENT_RE_PURCHASE_KEY, min, max);
        LOGGER.info("删除过期任务的个数,num:{}", num);
    }

    /**
     * 获得发送周期的时间戳
     * RePurchaseServiceImpl.getendCyclesStamp()
     *
     * @return
     *
     * @Author fanjd
     * @Date 2017年6月22日
     * @since 1.0.0
     */
    private long getSendCycleTimeStamp() {
        String sendCycle = ConfigUtil.getString("re_purchase_send_cycle");
        // 发送周期的时间戳(发送周期*1天的毫秒数)
        long sendCycleTimeStamp = Integer.parseInt(sendCycle) * 24 * 60 * 60 * 1000L;
        return sendCycleTimeStamp;
    }

    /**
     * 获取当天0点的时间戳
     * RePurchaseServiceImpl.getNowTimeStamp()
     *
     * @return
     *
     * @Author fanjd
     * @Date 2017年6月22日
     * @since 1.0.0
     */
    private long getNowTimeStamp() {
        // 获取当天0点的时间戳
        Calendar c = Calendar.getInstance();
        c = DateUtil.getStartTime(c);
        long now = c.getTimeInMillis();
        return now;
    }

    /**
     * 计算下次发送时间的时间戳
     * RePurchaseServiceImpl.nextSendTimeStamp()
     *
     * @param sendTime 当前发送的时间戳
     *
     * @return
     *
     * @Author fanjd
     * @Date 2017年6月22日
     * @since 1.0.0
     */
    private long nextSendTimeStamp(long sendTime) {
        long sendCycleTimeStamp = this.getSendCycleTimeStamp();
        // 计算下次发送的时间戳
        long nextSendTimeStamp = sendTime + sendCycleTimeStamp;
        return nextSendTimeStamp;
    }
}
